'use strict'

const http = require('http')
const https = require('https')
const helper = require('./helper')
const url = require('url')
const fs = require('fs')
const moveFile = require('./movefile')

class http1 {
  constructor (options = {}) {
    this.config = options.config
    this.router = options.router
    this.midware = options.midware
    this.events = options.events
    this.service = options.service
    this.handle = null
    if (options.requestHandle) {
      this.handle = options.requestHandle
    }
  }

  context () {
    var ctx = {
      version : '1.1',
      maxBody : 0,
      method    : '',
      ip      : '',
      host    : '',
      protocol: '',
      //实际的访问路径
      path    : '',
      name    : '',
      headers   : {},
      //实际执行请求的路径
      routepath   : '',
      param     : {},
      query     : {},
      body    : {},
      isUpload  : false,
      group     : '',
      rawBody   : '',
      bodyBuffer  : [],
      bodyLength  : 0,
      files     : {},
      exec : null,
      helper    : helper,
      port    : 0,

      res : {
        body : '',
        encoding : 'utf8',
      },
  
      request   : null,
      response  : null,
  
      box : {},
      service:null,
    }

    ctx.send = (d) => {
      ctx.res.body = d
    }

    ctx.getFile = (name, ind = 0) => {
      if (ind < 0) {return ctx.files[name] || [];}
  
      if (ctx.files[name] === undefined) {return null;}
      
      if (ind >= ctx.files[name].length) {return null;}
  
      return ctx.files[name][ind]
    };
  
    ctx.setHeader = (name, val) => {
      ctx.response.setHeader(name, val)
    };
  
    ctx.status = (stcode = null) => {
      if (stcode === null) { return ctx.response.statusCode }
      if(ctx.response) { ctx.response.statusCode = stcode }
    };

    ctx.moveFile = (upf, target) => {
      return moveFile(ctx, upf, target)
    }

    ctx.write = (data, encoding='utf8') => {
      return ctx.response.write(data, encoding)
    }
    
    return ctx
  }

  /**
   * request事件的回调函数。
   */
  onRequest () {
    var self = this

    var protocol = self.config.https ? 'https:' : 'http:'

    var callback = (req, res) => {

      if (typeof self.handle === 'function') {
        self.handle(req, res, protocol)
      }

      req.on('abort', (err) => {
        res.statusCode = 400
        res.end()
      })

      req.on('error', (err) => {
        res.statusCode = 400
        res.end()
        req.destroy()
      });

      let remote_ip = req.headers['x-real-ip'] || req.socket.remoteAddress

      let urlobj = url.parse(req.url, true)

      if (urlobj.pathname == '') {
        urlobj.pathname = '/'
      }

      //这时候如果不在HTTP支持的请求方法内的错误请求根本就不会去执行
      let rt = self.router.findRealPath(urlobj.pathname, req.method)
      if (rt === null) {
        res.statusCode = 404
        res.end(self.config.notFound)
        req.destroy()
        return
      }

      req.setTimeout(self.config.timeout)
      res.setTimeout(self.config.timeout)

      let ctx = self.context()
      ctx.bodyLength = 0
      ctx.maxBody = self.config.maxBody
      ctx.service = self.service
      
      ctx.method = req.method
      ctx.host = req.headers['host']
      ctx.protocol = protocol
      ctx.ip = remote_ip
      ctx.port = req.socket.remotePort

      ctx.request = req
      ctx.response = res
      ctx.headers = req.headers
      ctx.path = urlobj.pathname
      ctx.query = urlobj.query

      ctx.routepath = rt.key
      ctx.exec = rt.reqcall.reqCall
      ctx.name = rt.reqcall.name
      ctx.group = rt.reqcall.group
      ctx.param = rt.args
      rt = null

      return self.midware.go(ctx).finally(()=>{ ctx = null; })
    };

    return callback
  }

  middleware () {
    let self = this

    return async (ctx, next) => {

      if (ctx.method=='GET' || ctx.method=='OPTIONS' || ctx.method=='HEAD' || ctx.method=='TRACE') {
        ctx.request.on('data', data => {
          ctx.response.statusCode = 400
          ctx.response.end(self.config.badRequest)
          ctx.request.destroy()
        })

      } else {
        //ctx.method=='POST'||ctx.method=='PUT'||ctx.method=='DELETE'||ctx.method=='PATCH'
        if (ctx.box.dataHandle !== undefined && typeof ctx.box.dataHandle === 'function') {
          ctx.request.on('data', ctx.box.dataHandle)
        } else {
          ctx.request.on('data', data => {
            ctx.bodyLength += data.length
            if (ctx.bodyLength > ctx.maxBody) {
              ctx.bodyBuffer = null
              ctx.response.statusCode = 413
              //`Body too large,limit:${ctx.maxBody}Bytes`
              ctx.response.end('Request Entity Too Large')
              ctx.request.destroy()
              return 
            }
            ctx.bodyBuffer.push(data)
          });
        }

      }

      await new Promise((rv, rj) => {
        ctx.request.on('end',() => {
          if (ctx.request.aborted || ctx.response.finished) { 
            rj()
          } else {
            rv()
          }
        });
      })
      .then(async () => {
        if (ctx.bodyBuffer.length > 0) {
          ctx.rawBody = Buffer.concat(ctx.bodyBuffer, ctx.bodyLength)
        }
        ctx.bodyBuffer = null
        await next()
      }, err => {})

    }

  }

  /** 
   * 运行HTTP/1.1服务
   * @param {number} port 端口号
   * @param {string} host IP地址，可以是IPv4或IPv6
   * 0.0.0.0 对应使用IPv6则是::
  */
  run (port, host) {
    let self = this
    let serv = null

    if (this.config.https) {
      try {
        //如果是sni，就不需要这一项
        if (this.config.key && this.config.cert) {
          this.config.server.cert = fs.readFileSync(this.config.cert)
          this.config.server.key = fs.readFileSync(this.config.key)
        }
        
        serv = https.createServer(this.config.server, this.onRequest())

        serv.on('tlsClientError', (err, tls) => {
          if (self.config.debug) {
            console.error('--DEBUG-TLS-ERROR:', err)
          }
          tls.destroy()
        });

      } catch(err) {
        console.error(err)
        process.exit(-1)
      }
    } else {
      serv = http.createServer(this.onRequest())
    }

    serv.on('clientError', (err, sock) => {
      if (!sock.destroyed) {
        sock.end('HTTP/1.1 400 Bad request\r\n', () => {
          sock.destroy()
        });
      }
    });

    serv.on('error', (e) => {
      if (e.code === 'EADDRINUSE') {
        if (process.send !== undefined && typeof process.send === 'function') {
          process.send({type: '_eaddr'}, (err) => {})
        } else {
          console.error('该端口已被使用，请先停止进程')
        }
      }
    });
    
    serv.setTimeout(this.config.timeout, (sock) => {
      if (!sock.destroyed) {
        sock.end('HTTP/1.1 408 Request timeout\r\n', () => {
          sock.destroy()
        });
      }
    });
    
    for(let k in this.events) {
      if (typeof this.events[k] !== 'function') { continue; }
      if (k=='tlsClientError' || k == 'error') { continue; }
      serv.on(k, this.events[k])
      delete this.events[k]
    }
   
    //说明是使用unix socket模式监听服务
    if (typeof port === 'string' && port.indexOf('.sock') > 0) {
      serv.listen(port)
    } else {
      serv.listen(port, host)
    }

    return serv
  }

}

module.exports = http1
